package com.naiterui.ehp.bs.file.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.naiterui.ehp.bp.support.utils.FileUtil;
import com.naiterui.ehp.bp.utils.lang.ConfigUtil;
import com.naiterui.ehp.bp.utils.net.IPUtil;
import com.naiterui.ehp.bs.file.api.bo.FileBO;
import com.naiterui.ehp.bs.file.api.bo.FileHeaderBO;
import com.naiterui.ehp.bs.file.api.bo.FileParamBO;
import com.naiterui.ehp.bs.file.api.common.FileConstant;
import com.naiterui.ehp.bs.file.api.exception.FileException;
import com.naiterui.ehp.bs.file.api.exception.FileExceptionCodeEnum;
import com.naiterui.ehp.bs.file.config.WhiteAndBlackListConfig;
import com.naiterui.ehp.bs.file.domain.FileStore;
import com.naiterui.ehp.bs.file.repository.FileStoreRepository;
import com.naiterui.ehp.bs.file.service.FileService;
import com.naiterui.ehp.bs.file.utils.FileStoreUtil;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 文件相关
 * 
 * @author yangfuya
 * @since 2021/9/9
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class FileServiceImpl implements FileService {
    private final FileStoreRepository fileStoreRepository;
    private final WhiteAndBlackListConfig whiteAndBlackListConfig;

    /** 文件Remoter */
    public static final String fileHost = ConfigUtil.getString("ehp.domain.fileHost");
    /** 预览文件Remoter */
    public static final String filePreviewUrl = ConfigUtil.getString("ehp.remoter.previewUrl");
    /** 下载文件Remoter */
    public static final String fileDownloadUrl = ConfigUtil.getString("ehp.remoter.downloadUrl");
    /** url层级 */
    public static final int URL_LEVEL = 7;

    @Override
    public FileStore getFileStore(String uuid, String ec, String timestamp, Integer tc, String sign,
        HttpServletRequest request)
        throws FileException {
        FileStore fileStore = fileStoreRepository.get(uuid);
        if (ObjectUtil.isNull(fileStore)) {
            throw new FileException(FileExceptionCodeEnum.FILE_NOT_FOUND);
        }
        List<String> whitelist = whiteAndBlackListConfig.getWhitelist();
        List<String> blacklist = whiteAndBlackListConfig.getBlacklist();
        String fileStoreUid = fileStore.getUid();
        // 检查白名单
        if (CollUtil.isNotEmpty(whitelist) && FileStoreUtil.checkWhiteOrBlackList(whitelist, fileStoreUid)) {
            return fileStore;
        }
        // 检查黑名单
        if (CollUtil.isNotEmpty(blacklist) && FileStoreUtil.checkWhiteOrBlackList(blacklist, fileStoreUid)) {
            log.info("Resource is not accessible request ip: {}, ua: {},referer: {}", IPUtil.getIp(request),
                request.getHeader("User-Agent"), request.getHeader("Referer"));
            throw new FileException(FileExceptionCodeEnum.FILE_NOT_ACCESSIBLE);
        }
        if (!FileStoreUtil.check(ec, timestamp, tc, sign, request)) {
            log.info("sign check error request ip: {}, ua: {}, referer: {}", IPUtil.getIp(request),
                request.getHeader("User-Agent"), request.getHeader("Referer"));
            throw new FileException(FileExceptionCodeEnum.FILE_UNAUTHORIZED);
        }
        return fileStore;
    }

    @Override
    public FileBO generatePreview(FileParamBO fileParamBO, String operator) {
        String filePath = fileParamBO.getFilePath();
        if (StringUtils.isEmpty(filePath)) {
            return FileBO.builder().build();
        }

        FileStore fileStore = fileStoreRepository.findFirstByUid(filePath);
        if (fileStore == null) {
            fileStore = buildFileStore(filePath, operator);
            fileStoreRepository.save(fileStore);
        }

        return FileBO.builder()
            .url(buildResultUrl(filePreviewUrl, fileStore.getId(), fileParamBO))
            .uid(fileStore.getUid()).build();
    }

    @Override
    public Map<String, FileBO> batchGeneratePreview(FileParamBO fileParamBO, String operator) {
        List<String> filePaths = fileParamBO.getFilePaths();
        if (CollectionUtils.isEmpty(filePaths)) {
            return new HashMap<>();
        }
        // 先根据uid查询已有数据
        List<FileStore> fileStores = fileStoreRepository.findByUidIn(filePaths);
        Map<String, FileStore> fileStoreMap =
            fileStores.stream().collect(Collectors.toMap(FileStore::getUid, Function.identity()));

        // 添加不存在uid的数据
        List<FileStore> saveFileStores = new ArrayList<>();
        filePaths.stream().filter(filePath -> fileStoreMap.get(filePath) == null)
            .forEach(filePath -> saveFileStores.add(buildFileStore(filePath, operator)));
        fileStoreRepository.save(saveFileStores);
        fileStores.addAll(saveFileStores);

        return fileStores.stream().collect(Collectors.toMap(FileStore::getUid,
            fileStore -> FileBO.builder()
                .url(buildResultUrl(filePreviewUrl, fileStore.getId(), fileParamBO))
                .uid(fileStore.getUid()).build()));
    }

    @Override
    public FileBO generateDownload(FileParamBO fileParamBO, String operator) {
        String filePath = fileParamBO.getFilePath();
        if (StringUtils.isEmpty(filePath)) {
            return FileBO.builder().build();
        }

        FileStore fileStore = fileStoreRepository.findFirstByUid(filePath);
        if (fileStore == null) {
            fileStore = buildFileStore(filePath, operator);
            fileStoreRepository.save(fileStore);
        }

        return FileBO.builder()
            .url(buildResultUrl(fileDownloadUrl, fileStore.getId(), fileParamBO))
            .uid(fileStore.getUid()).build();
    }

    public FileStore buildFileStore(String filePath, String operator) {
        String name = FileUtil.getName(filePath);
        String fileName = FileUtil.getFileNameNoEx(name);
        String extensionName = FileUtil.getExtensionName(name);
        return FileStore.builder().uid(filePath).filename(fileName).suffix(extensionName)
            .storeType(FileStore.STORE_TYPE_NFS).createdAt(new Date()).createdBy(operator).build();
    }

    public String buildResultUrl(String remoter, String id, FileParamBO fileParamBO) {
        StringBuilder res = new StringBuilder(remoter);
        // 唯一标识
        res.append(id).append("/");
        // 加密控制字段
        FileHeaderBO fileHeader = fileParamBO.getFileHeader();
        String ipAc = fileParamBO.getIpCheck() ? "1" : "0";
        String uaAc = fileParamBO.getUaCheck() ? "1" : "0";
        String refererAc = fileParamBO.getRefererCheck() ? "1" : "0";
        String ec = ipAc + uaAc + refererAc;
        // 时间戳
        int defaultTimeLength = ConfigUtil.getInt("file.default.timeLength");
        Integer timeLength = fileParamBO.getTimeLength();
        String timestamp =
            FileStoreUtil.getTimestamp(timeLength == null || timeLength < 0 ? defaultTimeLength : timeLength);
        // 是否开启时间校验
        Integer timeCheck = fileParamBO.getTimeCheck() ? FileConstant.TIME_CHECK_OPEN : FileConstant.TIME_CHECK_CLOSE;
        res.append(ec).append("/").append(timestamp).append("/").append(timeCheck).append("/")
            .append(FileStoreUtil.sign(ec, fileHeader.getIp(), fileHeader.getUa(), fileHeader.getReferer(), timestamp));
        return res.toString();
    }

    @Override
    public String getRealPath(String url) {
        if (url.contains(fileHost)) {
            url = url.replace(fileHost, "");
        }
        String[] urlSp = url.split("/");
        if (urlSp.length < URL_LEVEL) {
            return "";
        }
        String uuid = urlSp[2];
        FileStore fileStore = fileStoreRepository.get(uuid);
        if (ObjectUtil.isNull(fileStore)) {
            return "";
        }
        return fileStore.getUid();
    }

    @Override
    public Map<String, FileBO> batchRealPath(List<String> urls) {
        List<String> uuids = new ArrayList<>();
        Map<String, String> urlMap = new HashMap<>();
        for (String url : urls) {
            String newUrl = url;
            if (newUrl.contains(fileHost)) {
                newUrl = newUrl.replace(fileHost, "");
            }
            String[] urlSp = newUrl.split("/");
            if (urlSp.length < URL_LEVEL) {
                continue;
            }
            String uuid = urlSp[2];
            uuids.add(uuid);
            urlMap.put(uuid, url);
        }
        if (CollUtil.isEmpty(uuids)) {
            return null;
        }
        List<FileStore> fileStoreList = fileStoreRepository.findByIdIn(uuids);
        if (CollUtil.isEmpty(fileStoreList)) {
            return null;
        }
        return fileStoreList.stream().map(f -> FileBO.builder().uid(f.getId()).url(f.getUid()).build())
            .collect(Collectors.toMap(f -> urlMap.get(f.getUid()), Function.identity()));
    }
}
